const win: any = globalThis || window;
/** 
 * 表格数据
 * (工具生成，请不要手动修改...)
 */
export namespace Xlsx {

    let b: any, Coder: any;
    /** 传入k-coder来进行初始化 */
    export function init(kcoder: any) {
        b = new kcoder.KBinary();
        Coder = kcoder.StructCoder;
    }

    /** 表数量 */
    export const count = 2;
    /** 已加载的表 */
    export const loadedDic: any = {};
    const dataDic: any = {};
    const prefix = "X";

    /** 读取表数据 */
    export function load(buf: Uint8Array, onCount?: (count: number) => void, onReload?: (reloadDic: { [tableName: string]: any }) => void) {
        let c = 0;
        b.rStart(buf);
        const reloadDic: any = Object.assign({}, loadedDic);
        while (b.rHasNext) {
            const tableName = b.rStr();
            const md5 = b.rStr();
            const isMisc = b.rUint8();
            const data = b.rCut(b.rAuint32());
            const loaded = loadedDic[tableName];
            if (loaded && loaded.$md5 === md5) {
                delete reloadDic[tableName];
            } else {
                dataDic[tableName] = data;
                const clsName = prefix + tableName;
                Object.defineProperty(win, clsName, {
                    get: () => loadedDic[tableName] = (Object.defineProperty(win, clsName, {
                        value: loadOne(clsName, tableName, md5, isMisc),
                        enumerable: true,
                        configurable: true
                    }) as any)[clsName],
                    enumerable: true,
                    configurable: true
                });
            }
            onCount && onCount(++c);
        }
        for (const name in reloadDic) {
            win[prefix + name];
        }
        b.rEnd();
        onReload && onReload(reloadDic);
    }

    const clsDic: any = {};
    function loadOne(clsName: string, tableName: string, md5: string, isMisc: number) {
        b.rStart(dataDic[tableName]);
        delete dataDic[tableName];

        const keyDic: any = {};
        const keyFields: string[] = [];
        if (!isMisc) {
            const keyLen = b.rUint8();
            for (let i = keyLen; --i >= 0;) {
                const key = b.rStr();
                keyDic[key] = true;
                keyFields.push(key);
            }
        }

        const fieldLen = b.rAuint32();
        const struct: any[] = [];
        for (let i = fieldLen; --i >= 0;) {
            const fieldName = b.rStr();
            const svt = b.rUint8();
            const arrVec = b.rUint8();
            const param = b.rUint8();
            struct.push([keyDic[fieldName] ? fieldName : fieldName + "?", svt, arrVec, param]);
        }
        const coder = new Coder(struct);

        if (isMisc) {
            const misc = coder.decode(b, loadedDic[tableName]);
            misc.$name = tableName;
            misc.$md5 = md5;
            return misc;
        }

        const Cls = clsDic[tableName] ||= function () { };
        Object.defineProperty(Cls, "name", { value: clsName, enumerable: true, configurable: true });
        Cls.$name = tableName;
        Cls.$md5 = md5;
        Cls.rowData = b.rCut(b.rAuint32());
        const getRows = () => {
            b.rStart(Cls.rowData);
            const rows: any[] = [];
            const rowLen = b.rAuint32();
            for (let i = 0; i < rowLen; ++i) {
                const row = coder.decode(b, new Cls());
                let dic = Cls.rowDic;
                for (let j = 0, len = keyFields.length - 1; j < len; ++j) {
                    const key = row[keyFields[j]];
                    dic = (dic[key] ||= {});
                }
                const key = row[keyFields[keyFields.length - 1]];

                if (dic[key]) {
                    for (let k in row) {
                        dic[key][k] = row[k];
                    }
                    rows.push(dic[key]);
                } else {
                    dic[key] = row;
                    rows.push(row);
                }
            }

            b.rEnd();
            delete Cls.rowData;
            Object.defineProperty(Cls, "rows", { value: rows, enumerable: true, configurable: true });
            return rows;
        }
        if (Cls.rowDic) {
            getRows();
        } else {
            Cls.rowDic = {};
            Object.defineProperty(Cls, "rows", { get: getRows, enumerable: true, configurable: true });
        }

        const rowDic = Cls.rowDic;
        Cls.get = (...keys: any[]) => {
            Cls.rows;
            switch (keys.length) {
                case 1: Cls.get = (k1: any) => rowDic[k1]; break;
                case 2: Cls.get = (k1: any, k2: any) => (rowDic[k1] && rowDic[k1][k2]); break;
                case 3: Cls.get = (k1: any, k2: any, k3: any) => (rowDic[k1] && rowDic[k1][k2] && rowDic[k1][k2][k3]); break;
                default:
                    Cls.get = (...keys: any[]) => {
                        let dic = rowDic;
                        for (let i = 0; i < keys.length; ++i) {
                            const key = keys[i];
                            if (i === keys.length - 1) {
                                return dic[key];
                            } else if (dic[key]) {
                                dic = dic[key];
                            }
                            return undefined;
                        }
                    };
            }
            return Cls.get(...keys);
        }

        return Cls;
    }
}

declare global {

    /** [error.xlsx -> Error] 错误码表 */
    class XError {
        /** 表格名称 */
        static readonly $name: string;
        /** 行数据 */
        static readonly rows: readonly XError[];
        /** 通过key值获取行数据 */
        static get: (code: number) => XError;
        /** 错误码 */
        readonly code: number;
        /** 消息内容 */
        readonly msg: string;
    }

    /** [snake_misc.xlsx -> SnakeMisc] 蛇杂项配置 */
    const XSnakeMisc: {
        /** 表格名称 */
        readonly $name: string;
        /** 初始大小 */
        readonly initSize: number;
        /** 初始长度 */
        readonly initLen: number;
        /** 初始蛇身间距 */
        readonly initSpace: number;
        /** 初始移动速度 */
        readonly initMoveSpd: number;
        /** 转弯内圈大小比例 */
        readonly rotSizeRate: number;
    }
}